All files / web/src/app/vision-training/[model] layout.tsx

0% Statements 0/73
0% Branches 0/1
0% Functions 0/1
0% Lines 0/73

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74                                                                                                                                                   
'use client'

import { type ReactNode, useEffect } from 'react'
import { notFound } from 'next/navigation'
import { css } from '../../../../styled-system/css'
import { PageWithNav } from '@/components/PageWithNav'
import { VisionTrainingNavSlot } from '../components/VisionTrainingNavSlot'
import { isValidModelType } from '../hooks/useModelType'

interface VisionTrainingLayoutProps {
  children: ReactNode
  params: { model: string }
}

/**
 * Vision Training Layout
 *
 * Layout wrapper for all vision training pages under /vision-training/[model]/.
 *
 * Provides:
 * - Minimal nav bar with hamburger menu via PageWithNav
 * - Contextual nav content (model selector + tabs) via VisionTrainingNavSlot
 * - Model param validation (redirects to 404 if invalid)
 *
 * Uses the same PageWithNav + navSlot pattern as arcade pages for consistent UX.
 */
export default function VisionTrainingLayout({ children, params }: VisionTrainingLayoutProps) {
  // Validate model param - show 404 for invalid models
  if (!isValidModelType(params.model)) {
    notFound()
  }

  // Prevent body scrolling on vision training pages
  useEffect(() => {
    const originalOverflow = document.body.style.overflow
    document.body.style.overflow = 'hidden'
    return () => {
      document.body.style.overflow = originalOverflow
    }
  }, [])

  return (
    <PageWithNav navSlot={<VisionTrainingNavSlot />}>
      <div
        data-component="vision-training-layout"
        className={css({
          // Fixed nav is position:fixed, so we need padding-top to push content below it
          // Then fill remaining viewport height
          pt: 'var(--app-nav-height, 72px)',
          height: '100vh',
          display: 'flex',
          flexDirection: 'column',
          bg: 'gray.900',
          color: 'gray.100',
          overflow: 'hidden',
        })}
      >
        <main
          data-element="vision-content"
          className={css({
            flex: 1,
            display: 'flex',
            flexDirection: 'column',
            minHeight: 0,
            overflow: 'hidden',
          })}
        >
          {children}
        </main>
      </div>
    </PageWithNav>
  )
}